Shimming is a technique that allows additional functionality to be inserted between an application programming interface (API) client (e.g., an application, driver) and an API service (e.g., supplied by an operating system). An API client application may be written to use a collection of externally provided services (APIs), which provide some well-described functionality. These API services reside external to the client program, for example, contained in a dynamically linked library (DLL).
One of the major benefits provided by external API services is that a client application can be built without including the API service code directly in the client application. In particular, such a scheme provides a way for applications to declare their usage of a particular API service, but defer binding to that API until the application is actually loaded for execution. This allows application code to be separated from the API service code and allows for modularity in the construction of the application run-time environment. External API services are said to be “imported” into client applications and have “load-time binding” to applications. Accordingly, an application declares its intent to use imported API services and in response, the compiler can implicitly generate a “stub” entry in the applications import address table (IAT). The IAT containing import stubs can be generated by the compiler. These IAT stubs identify the name of the import API and the external service or DLL that corresponds with the API. When the application is loaded or otherwise made ready for execution, load-time binding will be performed and the IAT stubs will be updated to reference the correct API services.
FIG. 1 illustrates a conventional system 100 for linking an application 110 and an API service provider 120. Application 110 comprises a code section 112 and an IAT 114. In the code section 112, there is a call to import a procedure, here Foo. IAT 114 contains a pointer to the address of the Foo procedure in the API service provider 120. Conventional user-mode application shimming techniques are based on manipulating the IAT table entries to effect the insertion of functionality. This can be accomplished by changing the imported API's entry in the application's IAT to point to shim code rather than the original API service code.
FIG. 2 illustrates a conventional user-mode utilization of a shim 130 between the application 110 and the API service provider 120. The shim 130 can be written to provide a “value added” benefit to the API service or it can completely replace the API service functionality and never call the original API service provider 120. User-mode applications run in a process which is essentially owned by the application. Accordingly, there is only one client, the application, in the process. This is not true for a system process where kernel-mode drivers execute. In a system process, API service providers, such as an operating system kernel, are called by a multitude of different and substantially unique drivers.
Turning briefly to FIG. 3, an exemplary system driver interaction is illustrated. Driver X 310 has code 312 which utilizes IAT 314 to import or link to Foo Procedure 332 operating system kernel 330. Driver Y 320 has code 322 that employs IAT 324 to import or link to VerifierFoo Procedure 334 in system kernel 330, which then calls Foo Procedure 332 also in the kernel 330. Both drivers were written to invoke Foo Procedure or API 332, but Driver X's linkage is different from that of Driver Y's linkage. Driver Y has had its Foo import shimmed by a built in shim, namely VerifierFoo, while Driver X is directly linked to the original Foo API in the kernel.
One important goal of shim developers is reusability. Thus, a good shim framework should support reuse of shims when possible. If a shim, which provides some extended service or fix is created then it is desirable that that shim be applied to all applications expressing the problem, for instance, that the shim was designed to correct. For example, if Shim X fixes problem X and applications A, B, and C have problem X, then it would be desirable to have Shim X be able to fix applications A, B, and C without any changes to Shim X. However, providing such a common shim has not been possible up to this point, due in part by the fact that different drivers often have different linkage configurations, such as Driver X and Driver Y supra. Moreover, conventional user-mode shims and shimming systems can retain only one linkage configuration, namely the most recent, while other linkage configurations associated with previously shimmed drivers is lost. Further complicating the problem is the fact that existing infrastructures for imported APIs or services do not readily provide any contextual information with respect to which driver is utilizing the API or service.
Accordingly, there is a need in the art for a shim system and method that can determine and maintain a plurality of linkage configurations, context, unique to each application or driver to be shimmed.